iT邦幫忙

2021 iThome 鐵人賽

DAY 21
2
Software Development

全端工程師生存筆記系列 第 21

[面試][設計模式]Code Review 會注意哪些事?會依照什麼原則對程式做 Refactoring?

  • 分享至 

  • xImage
  •  

在功能穩定後,你對程式碼還有要求嗎?

沒有最好的程式,只有更好的程式。」在完成功能後 Code Review 是非常重要的事情;不只看自己的程式碼,也要多觀摩其他人的程式碼,這個動作除了可以優化程式碼品質外,還可以幫助團隊了解彼此的工作進度

人無完人,很多時候第一時間想到的解法未必是最好的;解決問題的方法不只一種,可能有更簡潔的寫法或是更好的 Pattern 可以套用,只是開發者不具備這塊的知識儲備;此時 Code Review 就是提升團隊程式水平的方式。

找到需要改善的問題後,Refactoring(重構)就是另一個開始;如果為了盡快完成功能而埋頭狂寫,容易導致日後維護以及交接的困難性;專案越大這個問題會越明顯,所以在完成功能後對程式做 Refactoring 是必要的任務,它能提升你日後解決 Bug、需求變更時的工作效率

大綱

  1. Code Review 會注意哪些事?會依照什麼原則對程式做 Refactoring?

    • 1.1 面試官為什麼會問?
    • 1.2 面試官想從答案確認什麼?
    • 1.3 筆者提供的簡答
  2. 回答問題所需具備的知識

    • 2.1 SOLID 原則
    • 2.2 Design Pattern 是什麼?解決了什麼問題?
  3. 衍伸問題

    • 3.1 有處理 Legacy Code 的經驗嗎?
    • 3.2 有用什麼工具讓團隊有一致的 Coding Style

1. Code Review 會注意哪些事?會依照什麼原則對程式做 Refactoring?

1.1 面試官為什麼會問?

只要是軟體工程師的職位,這題可以說是超級常見面試題;因為它能問得很淺也能夠問得很深,如果是相對資淺的職位,求職者只要能說出關鍵字並給出基本解釋就能過關;如果是資深的位置,除了關鍵字外,面試官會繼續詢問你在專案中是否有實踐


1.2 面試官想從答案確認什麼?

  • 瞭解求職者 Code Review 會在意的點
  • 有使用哪些方法對程式做 Refactoring
  • 對 SOLID 原則有基礎認知
  • 知道 Design Pattern 且能舉出實務上的應用
  • 有用過什麼工具讓團隊有一致的 Coding Style

1.3 筆者提供的簡答

我會先觀察變數與函式的命名是否符合團隊規範、變數的宣告是否合理(var/let/const 的使用時機);然後看程式在設計上是否有符合 SOLID 原則;在功能完成後如果發現有合適的 Pattern 可以更好地解決問題,就會再對程式做 Refactoring。


2. 回答問題所需具備的知識

其他章節的知識,可能換一個時空背景就用不到了;但今天這個章節的內容,是每位開發人員都需要將其內化成為本能的

2.1 SOLID 原則

SOLID 是一個物件導向的設計原則,核心目標是為了讓程式碼更乾淨、易讀、好維護,讓程式朝著 Clean Code(無瑕的程式碼)的方向前進。

  • S(Single Responsibility Principle)單一責任原則
    一次只做好一件事;如果把系統比喻成樂高,那單一責任原則就是樂高的每一塊積木。
  • O(Open-Closed Principle)開放封閉原則
    程式的架構要容易擴充功能;而這個功能的擴充是新增程式,而非修改過去的程式,這麼做可以維持原有程式的穩定性。
  • L(Liskov Substitution Principle)里氏替換原則
    在繼承中衍生的子類別,要能完全繼承父類別的功能;實作時還需顧慮到單一責任原則,避免日後功能拆分或刪減時的困擾
    以軟體的角度來說,使用者期待版本從 1.0 ➞ 1.1 時行為是一致的,不會更新完舊功能就壞掉

    子類別 Override 時要注意

    • 不可改變父類別先決條件
      假設籃球比賽的參與者年齡限制在 15~80 歲;裡面可能區分為青年組、中年組、老年組,青年組的年齡「可以是 15~30」;但「不可以是 14~30」。
    • 子類別的後置條件不該被削弱
      籃球的得分有「2」分球、「3」分球,都是數字;如果某個比賽改用「三分球」這個字串紀錄得分就是不可以的。
  • I(Interface Segregation Principles)介面隔離原則
    設計程式時不應該預設使用者一定會使用到介面中的多個功能,我們應該盡量讓介面的功能單一;以後端 API 來舉例,我們不要讓一個 API 可以做好多事情,但做每件事情時只用到其中幾個參數
  • D(Dependency Inversion Principle)依賴反轉原則
    子類別一定會依賴父類別,這樣單向的關係是乾淨的;如果是雙向的依賴就會導致程式碼邏輯難以追蹤。

2.2 Design Pattern 是什麼?解決了什麼問題?

Pattern 是指在不同場景下的解釋,包含 Context(情境敘述)、Problem(問題)、Solution(解決方案)。

Design Pattern 就是過去人們發現解決問題的套路;學習越多 Design Pattern 在遇到問題時可以有更多的思路,並縮短與其他工程師討論的時間,因爲 Pattern 本身就包含了對問題的基礎解決方案,可以省下說明的時間。

下面分享一些常見的 Design Pattern 幫大家建立基礎概念,在看完後讀者也許會恍然大悟,原來我一直都有在使用 Pattern!

  • Factory Method 工廠方法
    簡述工廠負責生產客戶需要的產品
    假設今天有一個飲料工廠,他會生產很多種類的產品,像是「紅茶、綠茶、烏龍茶」;也許這些飲料的製作過程很複雜,但對客戶來講只要說出自己想要什麼口味的飲料就好了。

  • Strategy 策略
    簡述一個策略介面底下有很多靈活的方法供選擇
    把計算機的計算當成一個介面,這個介面提供加減乘除的方法;如果想加入次方、開根號的功能也能夠輕鬆擴充。

  • Factory Method 與 Strategy 混合運用
    簡述有時解決方案是多個 Pattern 的組合
    假設今天開一間飲料店,飲料會分成茶類、咖啡;每杯飲料又有冰量、甜度的選擇,這裡我們可以用 Factory Method 來建立產品;而飲料在販售時,不同組合有對應的折扣,這裡就可以用 Strategy 提供對應的方法來計算總金額。

  • Singleton 單例
    簡述希望在程式每個位置都能呼叫統一的 Instance
    在寫程式時會希望某些資源可以重複利用,並且在不同檔案引用時這些資料是一致的,像是共用的計時器、資料庫等物件。

  • Decorator 裝飾
    簡述將需求獨立,依據實際需求取得動態的結果
    假設一間餐廳提供三種餐點

    • 商業午餐(主餐)
    • 簡餐(主餐 + 飲料)
    • 套餐(主餐 + 飲料 + 甜點)

    我們發現上面餐點是有疊加性的,所以可以用繼承的方式來撰寫餐點間的關係;但如果今天老闆想要更換簡餐的內容組合(ex:贈送水果),而不想改變套餐的內容,就會導致程式必須改寫。
    面對這個問題,我們可以用 Decorator 作為解決方案,將主餐、飲料、甜點、水果各自獨立,依據商業午餐、簡餐、套餐實際的內容來做組合;這樣就能減少耦合性的問題。

  • Observer 觀察者
    簡述觀察者要能掌握被觀察者的狀態改變,較常用於 GUI 的設計
    像是 Vue.js 的 watch 就是用來監聽一個「值」的變化去做一系列的事情(ex:UI 上的調整)。

  • Command 命令
    簡述將請求的物件和執行的物件分開
    路邊攤的老闆通常是一個人負責備料、點餐、料理、清潔…等到生意做起來有店面了,他才會考慮雇用一些人手來幫忙內場、外場,如果把這個解決方案用 Command 來舉例:

    • 客人(Client):跟服務生說自己想點什麼餐(Concrete Command)。
    • 服務生(Invoker):紀錄客人的餐點(Concrete Command),並呼叫命令。
    • 廚師(Receiver):收到命令後,開始料理餐點(Concrete Command)。
  • Builder 建造者
    簡述依照自己的需求客製化產品
    一台筆電是由許多零件組裝而成,你可以選擇標配也能夠用選配來客製化它;為了讓選配的零件規格有彈性,Builder 就是一個很棒的解決方案;它讓你先選完自己所需的零件後,再產生最後的產品。

備註:如果你熟悉的 Pattern 剛好能解決目前遇到的問題那是皆大歡喜;萬一沒有合適的 Pattern 可以套用,千萬不要為了設計而設計;開發人員的目標應該是想透過 Pattern 解決問題,而不是從 Pattern 出發去設計問題。


3. 衍伸問題

3.1 有處理 Legacy Code 的經驗嗎?

除非是新創團隊或是接案類型的公司;不然絕大多數新人入職後都要接手前人遺留的程式碼(Legacy Code),如果求職者只有從零開始的經驗,在一開始會有陣痛期。

考點:了解求職者是否接觸過 Legacy Code、碰到時如何快速掌握

之前有因為專案擴編而去支援的經驗,因為當時有時程上的壓力;所以我在加入團隊後除了重點了解自己要負責的 Feature、團隊的 Coding Style 外,會主動向定下這些需求的 PM 討論,以此了解因果關係並快速融入團隊。

也曾接手過要維護及擴充的專案,因為當時沒有相關文件可以參考,為了快速掌握專案,我一邊操作系統一邊畫心智圖幫助自己釐清功能;了解系統架構後再去找合作窗口確認需求,避免實作方向與客戶需求不同。

在修改 Legacy Code 時,我會特別注意前人是否有撰寫測試案例,在程式能有效測試的狀態下才能安心的修改與擴充。

以正在運行的專案來說,我會希望建立團隊 Code Review 機制,讓每個人熟悉彼此的程式、使用共同的 Coding Style,當知識共享與風格統一後,就能減少 Legacy Code 的產生


3.2 有用什麼工具讓團隊有一致的 Coding Style

考點:是否有善用工具解決問題

我在 VSCode 有安裝一個 Prettier 的外掛,靠它就能非常有效的將程式碼格式化;不過有時為了保證團隊的 Coding Style 會導入 ESLint 來統一風格,但 Prettier 與 ESLint 一起使用時會遇到衝突,所以我有再安裝一個 Prettier ESLint 的外掛來解決這個問題;讓程式不僅符合團隊的 Coding Style,還可以透過快速鍵將程式碼格式化。


寫完這篇文章時,我的心裡突然冒出一段話:「一開始我們在追求問題的解答;到後來,我們在學習解決問題的方法。」

感謝大家的閱讀,如果喜歡我的文章可以訂閱接收通知;如果有幫助到你,按Like可以讓我更有寫文的動力,我們明天見~

參考資源

  1. 【翻譯】JavaScript 設計模式
  2. 什麼是 Design Pattern?
  3. 我為什麼想學設計模式 ( Design Pattern )
  4. 設計模式五大基本原則 SOLID

我在 Medium 平台 也分享了許多技術文章
❝ 主題涵蓋「MIS & DEVOPS資料庫前端後端MICROSFT 365GOOGLE 雲端應用自我修煉」希望可以幫助遇到相同問題、想自我成長的人。❞


https://ithelp.ithome.com.tw/upload/images/20230512/20103256twZPv1G4XH.jpg

在許多人的幫助下,本系列文章已成功出版,除了添加新的篇章,更完善了每個案例的應對進退;如果對現在的職涯感到迷茫,也許這本書能帶給你不一樣的觀點~

天瓏書局: https://www.tenlong.com.tw/products/9786263334571


上一篇
[面試][資料庫]如何解決高併發情境的商品秒殺問題
下一篇
[面試][系統設計]如何設計一個像 Facebook 的社交平台
系列文
全端工程師生存筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言